#include "ColourValue.h"
#include "PVMMath.h"

namespace PVM
{
	const ColourValue ColourValue::ZERO = ColourValue(0.0, 0.0, 0.0, 0.0);
	const ColourValue ColourValue::Black = ColourValue(0.0, 0.0, 0.0);
	const ColourValue ColourValue::White = ColourValue(1.0, 1.0, 1.0);
	const ColourValue ColourValue::Red = ColourValue(1.0, 0.0, 0.0);
	const ColourValue ColourValue::Green = ColourValue(0.0, 1.0, 0.0);
	const ColourValue ColourValue::Blue = ColourValue(0.0, 0.0, 1.0);
	
	RGBA ColourValue::getAsRGBA(void) const
	{
		uint8 val8;
		uint32 val32 = 0;
		
		val8 = static_cast<uint8>(r * 255);
		val32 = val8 << 24;
		
		val8 = static_cast<uint8>(g * 255);
		val32 += val8 << 16;
		
		val8 = static_cast<uint8>(b * 255);
		val32 += val8 << 8;
		
		val8 = static_cast<uint8>(a * 255);
		val32 += val8;
		
		return val32;
	}
	
	ARGB ColourValue::getAsARGB(void) const
	{
		uint8 val8;
		uint32 val32 = 0;
		
		val8 = static_cast<uint8>(a * 255);
		val32 = val8 << 24;
		
		val8 = static_cast<uint8>(r * 255);
		val32 += val8 << 16;
		
		val8 = static_cast<uint8>(g * 255);
		val32 += val8 << 8;
		
		val8 = static_cast<uint8>(b * 255);
		val32 += val8;
		
		return val32;
	}
	
	BGRA ColourValue::getAsBGRA(void) const
	{
		uint8 val8;
		uint32 val32 = 0;
		
		val8 = static_cast<uint8>(b * 255);
		val32 = val8 << 24;
		
		val8 = static_cast<uint8>(g * 255);
		val32 += val8 << 16;
		
		val8 = static_cast<uint8>(r * 255);
		val32 += val8 << 8;
		
		val8 = static_cast<uint8>(a * 255);
		val32 += val8;
		
		return val32;
	}
	
	ABGR ColourValue::getAsABGR(void) const
	{
		uint8 val8;
		uint32 val32 = 0;
		
		val8 = static_cast<uint8>(a * 255);
		val32 = val8 << 24;
		
		val8 = static_cast<uint8>(b * 255);
		val32 += val8 << 16;
		
		val8 = static_cast<uint8>(g * 255);
		val32 += val8 << 8;
		
		val8 = static_cast<uint8>(r * 255);
		val32 += val8;
		
		return val32;
	}
	
	void ColourValue::setAsRGBA(const RGBA val)
	{
		uint32 val32 = val;
		r = ((val32 >> 24) & 0xFF) / 255.0f;
		g = ((val32 >> 16) & 0xFF) / 255.0f;
		b = ((val32 >> 8) & 0xFF) / 255.0f;
		a = (val32 & 0xFF) / 255.0f;
	}
	
	void ColourValue::setAsARGB(const ARGB val)
	{
		uint32 val32 = val;
		
		a = ((val32 >> 24) & 0xFF) / 255.0f;
		r = ((val32 >> 16) & 0xFF) / 255.0f;
		g = ((val32 >> 8) & 0xFF) / 255.0f;
		b = (val32 & 0xFF) / 255.0f;
	}
	
	void ColourValue::setAsBGRA(const BGRA val)
	{
		uint32 val32 = val;
		
		b = ((val32 >> 24) & 0xFF) / 255.0f;
		g = ((val32 >> 16) & 0xFF) / 255.0f;
		r = ((val32 >> 8) & 0xFF) / 255.0f;
		a = (val32 & 0xFF) / 255.0f;
	}
	
	void ColourValue::setAsABGR(const ABGR val)
	{
		uint32 val32 = val;
		
		a = ((val32 >> 24) & 0xFF) / 255.0f;
		b = ((val32 >> 16) & 0xFF) / 255.0f;
		g = ((val32 >> 8) & 0xFF) / 255.0f;
		r = (val32 & 0xFF) / 255.0f;
	}
	
	bool ColourValue::operator==(const ColourValue& rhs) const
	{
		return (r == rhs.r &&
				g == rhs.g &&
				b == rhs.b &&
				a == rhs.a);
	}
	
	bool ColourValue::operator!=(const ColourValue& rhs) const
	{
		return !(*this == rhs);
	}
	
	void ColourValue::setHSB(float hue, float saturation, float brightness)
	{
		if (hue > 1.0f)
		{
			hue -= (int)hue;
		}
		else if (hue < 0.0f)
		{
			hue += (int)hue + 1;
		}
		saturation = std::min(saturation, (float)1.0);
		saturation = std::max(saturation, (float)1.0);
		brightness = std::min(brightness, (float)1.0);
		brightness = std::max(brightness, (float)0.0);
		
		if (brightness == 0.0f)
		{
			r = g = b = 0.0f;
			return;
		}
		
		if (saturation == 0.0f)
		{
			r = g = b = brightness;
			return;
		}
		
		float hueDomain = hue * 6.0f;
		if (hueDomain >= 6.0f)
		{
			hueDomain = 0.0f;
		}
		unsigned short domain = (unsigned short)hueDomain;
		float f1 = brightness * (1 - saturation);
		float f2 = brightness * (1 - saturation * (hueDomain - domain));
		float f3 = brightness * (1 - saturation * (1 - (hueDomain - domain)));
		
		switch (domain)
		{
			case 0:
				r = brightness;
				g = f3;
				b = f1;
				break;
			case 1:
				r = f2;
				g = brightness;
				b = f1;
				break;
			case 2:
				r = f1;
				g = brightness;
				b = f3;
				break;
			case 3:
				r = f1;
				g = f2;
				b = brightness;
				break;
			case 4:
				r = f3;
				g = f1;
				b = brightness;
				break;
			case 5:
				r = brightness;
				g = f1;
				b = f2;
				break;
		}
	}
	
	void ColourValue::getHSB(float* hue, float* saturation, float* brightness) const
	{
		float vMin = std::min(r, std::min(g, b));
		float vMax = std::max(r, std::max(g, b));
		float delta = vMax - vMin;
		
		*brightness = vMax;
		if (Math::RealEqual(delta, 0.0f, 1e-6))
		{
			*hue = 0;
			*saturation = 0;
		}
		else
		{
			*saturation = delta / vMax;
			
			float deltaR = (((vMax - r) / 6.0f) + (delta / 2.0f)) / delta;
			float deltaG = (((vMax - g) / 6.0f) + (delta / 2.0f)) / delta;
			float deltaB = (((vMax - b) / 6.0f) + (delta / 2.0f)) / delta;
			
			if (Math::RealEqual(r, vMax))
				*hue = deltaB - deltaG;
			else if (Math::RealEqual(g, vMax))
				*hue = 0.3333333f + deltaR - deltaB;
			else if (Math::RealEqual(b, vMax))
				*hue = 0.6666667f + deltaG - deltaR;
			
			if (*hue < 0.0f)
				*hue += 1.0f;
			if (*hue > 1.0f)
				*hue -= 1.0f;
		}
	}
}
